package com.powergisol.gis.controller;

import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSONObject;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.powergisol.gis.entity.TabService;
import com.powergisol.gis.service.FallBackService;
import com.powergisol.gis.service.MondbDataService;
import com.powergisol.gis.service.TabDataService;
import com.powergisol.gis.util.DataChangeUtil;
import com.powergisol.gis.util.FileSuffixRename;
import com.powergisol.gis.util.PropertyUtil;
import com.powergisol.gis.util.Result;
import com.powergisol.gis.vo.FeatureType;
import com.powergisol.gis.vo.TabServiceVo;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPOutputStream;

import static java.lang.Integer.valueOf;

/**
 * Date: 2019/7/18  14:25
 * Description: 服务展示
 */
@Controller
public class TDModelController {
    private static Logger logger = LoggerFactory.getLogger(TDModelController.class);
    @Autowired
    private GridFsOperations operations;

    @Autowired
    private GridFSBucket gridFSBucket;

    @Autowired
    private TabDataService tabDataService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MongoDbFactory mongoDbFactory;
    @Autowired
    private FallBackService fallBackService;
    @Autowired
    private MondbDataService mondbDataService;

//    @Value("${file-url}")
//    private String fileUrl;
//    @Value("${style-url}")
//    private String styleUrl;

    @Value("${expira_time}")
    private Integer expiraTime;

    @RequestMapping(value = "gis/3DModel/**", method = RequestMethod.GET)
    public Object readFile1(String uuid,String serviceName,HttpServletRequest request,HttpServletResponse response,Model model){
        //logger.info("参数uuid={}，serviceName={}",uuid,serviceName);
        if(null == uuid){
            return "forward:wms";
        }
        Result result = new Result();
        TabService tabService= tabDataService.getByName(serviceName);
        if(tabService.getState()!=1){
            logger.info("服务不可用:{}",serviceName);
            return result.failure(-1,"服务不可用");
        }
        if(null !=uuid && !redisTemplate.hasKey(uuid)){
            tabService.setVisitTimes(tabService.getVisitTimes()+1);
            tabDataService.updateTabService(tabService);
            redisTemplate.opsForValue().set(uuid,uuid,expiraTime,TimeUnit.HOURS);
        }
        String url = request.getRequestURI();
        String fileUrl = tabService.getFileUrl();
        String filePath = null;
        switch (tabService.getServiceType()){
            case 7:
                filePath = fileUrl.substring(fileUrl.lastIndexOf(File.separator)+1,fileUrl.length());
                break;
            case 6:
                if(!url.contains(".")){//返回对应的json
                    filePath = fileUrl.substring(fileUrl.lastIndexOf(File.separator)+1,fileUrl.length());
                }else{
                    filePath = url.substring(url.indexOf("/service/gis/3DModel/")).replace("/service/gis/3DModel/","");
                }
                break;
            default :
                if(!url.contains(".")){//返回对应的json
                    return "forward:data?fileUrl="+fileUrl;
                }else{
                    filePath = url.substring(url.indexOf("/service/gis/3DModel/")).replace("/service/gis/3DModel/","");
                }
                break;
        }
        String absolutePath = null;
        if(tabService.getServiceType()==8 || tabService.getServiceType()==5){
            absolutePath = fileUrl.substring(0,fileUrl.lastIndexOf(File.separator));
            absolutePath = absolutePath.substring(0,absolutePath.lastIndexOf(File.separator))+File.separator+filePath;
        }else{
            absolutePath = fileUrl.substring(0,fileUrl.lastIndexOf(File.separator))+File.separator+filePath;
        }
        File file = new File(absolutePath);
        if(file.exists()){
            readFileLocal(absolutePath,response);
        }else{
            readFileAndWriteDisk(tabService.getUuid()+File.separator+filePath,response,absolutePath);
        }
        return null;
    }

    @GetMapping(value = "gis/3DModel/readFile")
    @ResponseBody
    public Object readFile(String serviceName) {
        GridFSDownloadStream stream = null;
        try {
            String filename = SecureUtil.md5(serviceName.replace("/", File.separator));
            GridFSFile gridFSFile = operations.findOne(new Query(Criteria.where("filename").is(filename)));
            if(null == gridFSFile){
                logger.info("{} 在数据库中未找到文件,MD5值={}",serviceName,filename);
                return null;
            }
            ObjectId objectId = gridFSFile.getObjectId();
            stream = gridFSBucket.openDownloadStream(objectId);
            /** gridfs file */
            GridFSFile file = stream.getGridFSFile();

            /** chunk size */
            int size = file.getChunkSize();
            Long len = file.getLength();
            /** loop time */
            BigDecimal num = new BigDecimal(len).divide(new BigDecimal(size),0,BigDecimal.ROUND_UP);
            Integer cnt = valueOf(num.toString());
            byte[] bts = new byte[size];

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            while (cnt-- > 0) {
                int tmp = stream.read(bts);
                baos.write(bts, 0, tmp);
            }
            byte[] buffer = baos.toByteArray();

            return buffer;
        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            if (stream != null) stream.close();
        }
        return null;
    }

    @GetMapping(value = "gis/3DModel/data")
    @ResponseBody
    public Result wms(String fileUrl) {
        Result result = new Result();
        String serviceName = getUrl(fileUrl);
        logger.info("返回对应json路径：{}",serviceName);
        result.setData(serviceName);
        return result;
    }

    @GetMapping(value = "gis/3DModel/wms")
    public String wms(Model model, String serviceName) {
        String suffix = "html";
        if(null == serviceName){
            model.addAttribute("detailsData", null);
            return "noMessage";
        }
        String[] split = serviceName.split("\\.");
        String name = split[0];
        if (split.length == 2) {
            suffix = split[1];
        }
        TabServiceVo tabService = tabDataService.getVoByName(name);
        if(1 != tabService.getState()){
            model.addAttribute("detailsData", tabService);
            return "noMessage";
        }
        FeatureType dModel = getModel(name);
        model.addAttribute("baseData", dModel);
        model.addAttribute("detailsData", tabService);
        switch (suffix) {
            case "xml":
                return "noCreating";
            case "json":
                return "noCreating";
            case "kml":
                return "noCreating";
            case "ol3":
                return "ol";
            case "webgl3d":
                return "map";
            default:
                return "index";
        }
    }

    @GetMapping("getFileIn")
    public void getFileIn(String uuid,HttpServletResponse response){
        TabService tabService = tabDataService.getByUuid(uuid);
        logger.info("从mongodb中读取数据返回流={},{}",tabService.getFileUrl(),uuid);
        String key = "";
        switch (tabService.getServiceType()){
            case 5://b3dm三维模型文件（*.json）
            case 8://lob三维模型文件（*.json）
                key = getUrl(tabService.getFileUrl());
                break;
            case 6://osgb三维模型文件（*.xml）
                key = tabService.getFileUrl().substring(tabService.getFileUrl().lastIndexOf(File.separator)+1,tabService.getFileUrl().length());
                break;
        }
        readFile(uuid+File.separator + key,response);
    }

    private  FeatureType getModel(String name){
        //logger.info("getModel 服务名称{}",name);
        TabServiceVo tabService = tabDataService.getVoByName(name);
        if(tabService.getState()!=1 || null == tabService.getFileUrl()){
            logger.info("服务不可用={},状态不为1或fileUrl为空", name);
            return null;
        }
        FeatureType featureType = null;

        switch (tabService.getServiceType()){
            case 3:///高程缓存文件（*.tif）转terrain 获取tif属性
                String userDir = System.getProperty("user.dir");
                userDir = userDir.substring(0,userDir.lastIndexOf(File.separator));

                String fileUrl1  = tabService.getOriginUrl();
                if(null == fileUrl1){
                    fileUrl1 = userDir+File.separator+"serviceData"+File.separator+"formal"+File.separator+"tif"+File.separator+tabService.getUuid()+File.separator+"layer.tif";
                }
                String result = fallBackService.getLayer(name,fileUrl1,userDir+File.separator+"serviceData"+File.separator+"style"+File.separator+tabService.getStyleUrl(),tabService.getTitle());
                if(null == result){
                    return featureType;
                }
                JSONObject object = JSONObject.parseObject(result);
                featureType = DataChangeUtil.jsonToEntity(object);
                break;
            case 2://高程缓存文件（*.terrain）
                break;
            case 5://b3dm三维模型文件（*.json）
                //String b3dmUrl = mondbDataService.getFileUrl(tabService.getFileUrl(),tabService.getUuid());
                String b3dmUrl = tabService.getFileUrl();
                if(!new File(b3dmUrl).exists()){
                    b3dmUrl = mondbDataService.getFileUrl(tabService.getFileUrl(),tabService.getUuid());
                }
                if(null == b3dmUrl){
                    break;
                }
                featureType = DataChangeUtil.b3dmToEntity(b3dmUrl);
                break;
            case 8://lob三维模型文件（*.json）
                //String lobUrl = mondbDataService.getFileUrl(tabService.getFileUrl(),tabService.getUuid());
                String lobUrl = tabService.getFileUrl();
                if(null == lobUrl){
                    break;
                }
                featureType = DataChangeUtil.lobToEntity(lobUrl);
                break;
            case 6://osgb三维模型文件（*.xml）
                String changeType = tabService.getChangeType();
                String osgbUrl = mondbDataService.getFileUrl(tabService.getFileUrl(),tabService.getUuid());
                //String osgbUrl = tabService.getFileUrl();
                if(null == osgbUrl){
                    break;
                }
                switch (changeType){
                    case "b3dm":
                        featureType = DataChangeUtil.osgbTb3dmToEntity(osgbUrl);
                        break;
                    case "lob" :
                    case "pld" :
                        featureType = DataChangeUtil.lobToEntity(osgbUrl);
                        break;
                    default :
                        break;
                }
                break;
            case 7://obj三维模型文件（*.obj）
                break;
            default:
                break;
        }
        return featureType;
    }



    private String getUrl(String url){
        url = url.replace("\\","/");
        int index=url.lastIndexOf("/");
        index=url.lastIndexOf("/", index-1);
        return url.substring(index+1,url.length());
    }


    private void readFile(String serviceName, HttpServletResponse response){
        GridFSDownloadStream stream = null;
        try {
            String filename = SecureUtil.md5(serviceName.replace("/", File.separator));

            GridFSFile gridFSFile = operations.findOne(new Query(Criteria.where("filename").is(filename)));
            if(null == gridFSFile){
                logger.info("{} 在数据库中未找到文件,MD5值={}",serviceName,filename);
                return;
            }
            ObjectId objectId = gridFSFile.getObjectId();
            stream = gridFSBucket.openDownloadStream(objectId);
            /** gridfs file */
            GridFSFile file = stream.getGridFSFile();
            response.setContentType("application/form-data");
            //response.setCharacterEncoding("utf-8");
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Content-Type","application/form-data");
            OutputStream out = response.getOutputStream();
            /** chunk size */
            int size = file.getChunkSize();
            Long len = file.getLength();
            /** loop time */
            BigDecimal num = new BigDecimal(len).divide(new BigDecimal(size),0,BigDecimal.ROUND_UP);
            Integer cnt = valueOf(num.toString());
            byte[] bts = new byte[size];
//            response.setHeader("content-encoding", "gzip");
//            response.setContentLength(bts.length);

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            try {
                while (cnt-- > 0) {
                    int tmp = stream.read(bts);
                    out.write(bts, 0, tmp);
                }
                byte[] buffer = baos.toByteArray();

                out.flush();
                out.close();
            } catch (IOException e) {

            }

        } catch (IOException e) {
            e.printStackTrace();

        } finally {
            if (stream != null) stream.close();
        }
    }

    private void readFileAndWriteDisk(String serviceName, HttpServletResponse response,String absolutePath){
        GridFSDownloadStream stream = null;
        try {
            String filename = SecureUtil.md5(serviceName.replace("/", File.separator));

            GridFSFile gridFSFile = operations.findOne(new Query(Criteria.where("filename").is(filename)));
            if(null == gridFSFile){
                logger.info("{} 在数据库中未找到文件,MD5值={}",serviceName,filename);
                return;
            }
            ObjectId objectId = gridFSFile.getObjectId();
            stream = gridFSBucket.openDownloadStream(objectId);
            /** gridfs file */
            GridFSFile file = stream.getGridFSFile();
            response.setContentType("application/form-data");
            //response.setCharacterEncoding("utf-8");
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Content-Type","application/form-data");
            OutputStream out = response.getOutputStream();
            /** chunk size */
            int size = file.getChunkSize();
            Long len = file.getLength();

            tabDataService.writeToDisk(absolutePath,len,gridFSFile,size);
            /** loop time */
            BigDecimal num = new BigDecimal(len).divide(new BigDecimal(size),0,BigDecimal.ROUND_UP);
            Integer cnt = valueOf(num.toString());
            byte[] bts = new byte[size];
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            try {
                while (cnt-- > 0) {
                    int tmp = stream.read(bts);
                    out.write(bts, 0, tmp);
                }
                byte[] buffer = baos.toByteArray();
                out.flush();
                out.close();
            } catch (IOException e) {
            }

        } catch (IOException e) {
            e.printStackTrace();

        } finally {
            if (stream != null) stream.close();
        }
    }

    /**
     * 盘符读取文件
     * @param localPath
     * @param response
     */
    private void readFileLocal(String localPath, HttpServletResponse response){
        File filePic = new File(localPath);
        if(filePic.exists()){
            logger.info("有={}",localPath);
            ServletOutputStream out = null;
            FileInputStream ips = null;
            try{
                ips = new FileInputStream(filePic);
                int i = ips.available(); // 得到文件大小
                byte data[] = new byte[i];
                ips.read(data); // 读数据
                response.setContentType("multipart/form-data");
                response.setHeader("Access-Control-Allow-Origin", "*");
                response.setHeader("Content-Type","multipart/form-data");
                out = response.getOutputStream(); // 得到向客户端输出二进制数据的对象
                out.write(data); // 输出数据
                out.flush();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                try {
                    out.close();
                    ips.close();
                }catch (IOException e){

                }
            }
        }
    }
}
